package com.gitee.wsl.ext.list


/**
 * A function for replacing a value of immutable list.
 * @param replacement a replacement operation (takes an old element, returns a new element)
 * @param target checks whether an element should be replaced or not.
 * @return an updated list
 */
inline fun <T> List<T>.replace(replacement: (T) -> T, target: (T) -> Boolean): List<T> {
    return map { if (target(it)) replacement(it) else it }
}

/**
 * 使用一个新的元素替换符合条件的第一个元素
 *
 * @param element
 * @param predicate
 * @param T
 * @return 返回旧元素，如果存在的话
 * @receiver
 */
inline fun <T> MutableList<T>.replaceWith(element: T, predicate: (T) -> Boolean): T? {
    var old: T? = null
    this.indexOfFirst(predicate)
        .takeIf { it != -1 }
        ?.let { index ->
            old = this[index]
            this[index] = element
        }
    return old
}


/**
 * Returns a new List with all occurrences of [old] replaced with [new].
 *
 * The comparison between the elements is done non strict '=='
 */
fun <E> List<E>.replace(old: E, new: E): List<E> = map { if (it == old) new else it }

/**
 * Returns a new List with all occurrences of [old] replaced with [new].
 *
 * The comparison between the elements is done strict '==='
 */
fun <E> List<E>.replaceStrict(old: E, new: E): List<E> = map { if (it === old) new else it }

/**
 * Returns a new List with the element at [idx] replaced by [element].
 *
 * If the index is out of bounds an [IndexOutOfBoundsException] is thrown.
 */
fun <E> List<E>.replaceAt(idx: Int, element: E): List<E> = toMutableList().apply { set(idx, element) }.toList()

/**
 * Replaces the first item matched with [new] or adds it to the end of the list.
 *
 * All items are mapped by using [compareBy]. the comparison is done non-strict (==).
 */
fun <T, X> List<T>.replaceFirstByOrAdd(new: T, compareBy: (T) -> X): List<T> {

    val search = compareBy(new)

    return when (val idx = indexOfFirst { compareBy(it) == search }) {
        -1 -> plus(new)
        else -> replaceAt(idx, new)
    }
}
